//=============================================================================
/**
 * @file	arceus_gmk.c
 * @brief	シント遺跡ギミック
 * @author	Nozomu Saito
 *
 * モジュール名：ArceusGmk_
 *
 */
//=============================================================================

#include "common.h"
#include "fieldsys.h"
#include "fieldmap_work.h"
#include "include/savedata/gimmickwork.h"

#include "field_gimmick_def.h"
#include "arceus_gmk_local.h"
#include "arceus_gmk.h"
#include "fld_demo.naix"

#include "arceus_gmk_def.h"

#define DRAW_TCB_PRI	(1)	//描画ＴＣＢプライオリティ

#define ANM_NUM			(2)	//アニメ数
#define OBJ_NUM			(3)	//光るポイント数（ディア・パル・ギラ）

//３つの光る場所　地形依存
#define DIA_POINT_X	(200*FX32_ONE)
#define DIA_POINT_Z	(248*FX32_ONE)
#define PAL_POINT_X	(328*FX32_ONE)
#define PAL_POINT_Z	(248*FX32_ONE)
#define GIRA_POINT_X	(264*FX32_ONE)
#define GIRA_POINT_Z	(136*FX32_ONE)
#define POINT_Y			(16*FX32_ONE)

//アニメーションフレーム　アニメデータ依存
#define NORMAL_ANM_FRAME_END	(ARCEUS_GMK_FRAME*FX32_ONE)
#define LIGHT_ANM_FRAME_END		((ARCEUS_GMK_FRAME*2)*FX32_ONE)

typedef struct GMK_ANM_WORK_tag
{
	FLD_3DOBJ_MDL	Mdl;
	FLD_3DOBJ_ANM	Anm[ANM_NUM];
	FLD_3DOBJ		Obj;
	int NowType;
}GMK_ANM_WORK;

typedef struct GMK_TEMP_WORK_tag
{
	FIELDSYS_WORK *fsys;
	NNSFndAllocator Allocator;
	GMK_ANM_WORK	AnmWork[OBJ_NUM];

	int AnmChgReq;		//アニメ変更リクエストフラグ
/**
	FLD_3DOBJ_MDL	Mdl[OBJ_NUM];
	FLD_3DOBJ_ANM	Anm[OBJ_NUM][ANM_NUM];
	FLD_3DOBJ		Obj[OBJ_NUM];
	int NowType[OBJ_NUM];
*/
	TCB_PTR DrawTcb;
}GMK_TEMP_WORK;

static void Load3DData(GMK_TEMP_WORK *work);
static void Del3DData(GMK_TEMP_WORK *work);
static void DrawTcbFunc(TCB_PTR ptr, void * wk);
static BOOL AnimeLoop(GMK_ANM_WORK *anm_work, const u8 anm_num, const u8 inType);
static void SetAnmFrame(FLD_3DOBJ_ANM *anm, const u8 anm_num, const u32 inFrame);
static BOOL AnmChgWaitEvent(GMEVENT_CONTROL * event);

//---------------------------------------------------------------------------
/**
 * @brief	セットアップ
 *
 * @param	fsys	フィールドシステムポインタ
 *
 * @return	none
 */
//---------------------------------------------------------------------------
void ArceusGmk_Setup(FIELDSYS_WORK *fsys)
{
	int i;
	GIMMICKWORK *work;
	ARCEUS_GMK_WORK *a_gmk_work;
	GMK_TEMP_WORK *temp;

	//ここでギミックワークの中身を見る
	work = SaveData_GetGimmickWork(GameSystem_GetSaveData(fsys));
	a_gmk_work = (ARCEUS_GMK_WORK*)GIMMICKWORK_Get(work, FLD_GIMMICK_ARCEUS_GMK);

	//専用ワークをアロケートして、フィールドマップ汎用ワークポインタにあてる
	fsys->fldmap->Work = sys_AllocMemory(HEAPID_FIELD, sizeof(GMK_TEMP_WORK));

	//初期化
	MI_CpuClear8(fsys->fldmap->Work, sizeof(GMK_TEMP_WORK));
	temp = fsys->fldmap->Work;
	temp->fsys = fsys;

	//所定の3つの位置にＯＢＪをセット
	Load3DData(temp);

	//描画ＴＣＢを作成し保存
	temp->DrawTcb = TCB_Add( DrawTcbFunc, temp, DRAW_TCB_PRI );

}

//---------------------------------------------------------------------------
/**
 * @brief	ギミック終了処理
 *
 * @param	fsys	フィールドシステムポインタ
 *
 * @return	none
 */
//---------------------------------------------------------------------------
void ArceusGmk_End(FIELDSYS_WORK *fsys)
{
	GMK_TEMP_WORK *temp;
	temp = (GMK_TEMP_WORK *)fsys->fldmap->Work;

	//描画ＴＣＢ削除
	TCB_Delete(temp->DrawTcb);
	//3Ｄデータ削除
	Del3DData(temp);
	//ワーク解放
	sys_FreeMemoryEz(temp);
	fsys->fldmap->Work = NULL;
}

//==============================================================================
/**
 * すべてのＯＢＪを非表示にする
 *
 * @param	FIELDSYS_WORK *fsys
 *
 * @retval  none
 */
//==============================================================================
void ArceusGmk_Vanish(FIELDSYS_WORK *fsys)
{
	u8 i;
	GMK_TEMP_WORK *temp;
	temp = (GMK_TEMP_WORK *)fsys->fldmap->Work;
	for (i=0;i<OBJ_NUM;i++){
		//描画フラグ設定	(非表示)
		FLD_3DObjSetDraw( &temp->AnmWork[i].Obj, FALSE );
	}
}

//==============================================================================
/**
 * 指定のＯＢＪのアニメモードを変える
 *
 * @param	FIELDSYS_WORK *fsys
 * @param	inType			0:リセット 1:ディア 2:パル 3:ギラ
 *
 * @retval  none
 */
//==============================================================================
void ArceusGmk_ChgAnmMode(FIELDSYS_WORK *fsys, const u8 inType)
{
	GIMMICKWORK *work;
	ARCEUS_GMK_WORK *a_gmk_work;
	GMK_TEMP_WORK *temp;
	temp = (GMK_TEMP_WORK *)fsys->fldmap->Work;

	if (inType > OBJ_NUM){	//値は0〜3
		GF_ASSERT(0);
		return;
	}

	//ＯＢＪが非表示ならばアニメ変更を禁止（そういった状況にならないはず）
	//非表示状態は3つすべて非表示になるので、代表で0番インデックスのＯＢＪで調べる
	if ( !FLD_3DObjGetDraw( &temp->AnmWork[0].Obj ) ){
		GF_ASSERT_MSG(0,"現在ギミックは非表示状態。\n");
	}

	//ギミックワークの中身を見る
	work = SaveData_GetGimmickWork(GameSystem_GetSaveData(fsys));
	a_gmk_work = (ARCEUS_GMK_WORK*)GIMMICKWORK_Get(work, FLD_GIMMICK_ARCEUS_GMK);

	a_gmk_work->LightPoint = inType;

	//アニメ変更リクエスト
	temp->AnmChgReq = 1;

	//アニメ変更待ちイベントコール
	FieldEvent_Call(fsys->event, AnmChgWaitEvent, temp);
}

//==============================================================================
/**
 * 3Dデータロード
 *
 * @param	work			ワークポインタ
 * @retval  none
 */
//==============================================================================
static void Load3DData(GMK_TEMP_WORK *work)
{
	u8 i,j;
	int heap = HEAPID_FIELD;
	const int anm[OBJ_NUM][ANM_NUM] = {
		{
			NARC_fld_demo_sin_en_dl_nsbca,
			NARC_fld_demo_sin_en_dl_nsbta,
		},
		{
			NARC_fld_demo_sin_en_pl_nsbca,
			NARC_fld_demo_sin_en_pl_nsbta,
		},
		{
			NARC_fld_demo_sin_en_gr_nsbca,
			NARC_fld_demo_sin_en_gr_nsbta,
		},
	};

	const int mdl[OBJ_NUM] = {
		NARC_fld_demo_sin_en_dl_nsbmd,
		NARC_fld_demo_sin_en_pl_nsbmd,
		NARC_fld_demo_sin_en_gr_nsbmd,
	};

	// アロケーター作成
	sys_InitAllocator( &work->Allocator, heap, 32 );

	//モデルロード
	for (i=0;i<OBJ_NUM;i++){
		FLD_3DObjMdlLoad(&work->AnmWork[i].Mdl, ARC_FLD_DEMO_GRA , mdl[i], heap);
	}

	//アニメデータロード
	for (j=0;j<OBJ_NUM;j++){
		for (i=0;i<ANM_NUM;i++){
			FLD_3DObjAnmLoad(	&work->AnmWork[j].Anm[i], &work->AnmWork[j].Mdl, ARC_FLD_DEMO_GRA, anm[j][i],
								heap, &work->Allocator );
		}
	}

	//描画オブジェクト初期化
	for (i=0;i<OBJ_NUM;i++){
		FLD_3DObjInit( &work->AnmWork[i].Obj, &work->AnmWork[i].Mdl );
	}

	//アニメをリンク
	for (j=0;j<OBJ_NUM;j++){
		for (i=0;i<ANM_NUM;i++){
			FLD_3DObjAddAnm( &work->AnmWork[j].Obj, &work->AnmWork[j].Anm[i] );
		}
	}

	for (i=0;i<OBJ_NUM;i++){
		//描画フラグ設定	(表示)
		FLD_3DObjSetDraw( &work->AnmWork[i].Obj, TRUE );
		//フレームセット
		SetAnmFrame(work->AnmWork[i].Anm, ANM_NUM, 0);
	}

	//表示位置セット
	{
		//地形絶対位置に表示
		const VecFx32 vec[OBJ_NUM] = {
			{DIA_POINT_X, POINT_Y, DIA_POINT_Z},
			{PAL_POINT_X, POINT_Y, PAL_POINT_Z},
			{GIRA_POINT_X, POINT_Y, GIRA_POINT_Z},
		};

		for (i=0;i<OBJ_NUM;i++){
			FLD_3DObjSetMatrix( &work->AnmWork[i].Obj, vec[i].x, vec[i].y, vec[i].z );
		}
	}
}

//--------------------------------------------------------------
/**
 * 3Ｄデータ解放関数
 * @param	work	ワークポインタ
 * @return	none
 */
//--------------------------------------------------------------
static void Del3DData(GMK_TEMP_WORK *work)
{
	u8 i,j;
	for (j=0;j<OBJ_NUM;j++){
		//アニメデータ破棄
		for (i=0;i<ANM_NUM;i++){
			FLD_3DObjAnmDelete( &work->AnmWork[j].Anm[i], &work->Allocator);
		}
		//モデルデータ破棄
		FLD_3DObjMdlDelete( &work->AnmWork[j].Mdl);
	}
}

//--------------------------------------------------------------
/**
 * 描画ＴＣＢ
 * @param	ptr		ＴＣＢポインタ
 * @param	wk	ワークポインタ
 * @return	none
 */
//--------------------------------------------------------------
static void DrawTcbFunc(TCB_PTR ptr, void * wk)
{
	u8 i;
	GMK_TEMP_WORK *temp = (GMK_TEMP_WORK*)wk;
	GIMMICKWORK *work;
	ARCEUS_GMK_WORK *a_gmk_work;

	u8 mode[OBJ_NUM] = {0,0,0,};

	BOOL rc;

	//ギミックワークの中身を見る
	work = SaveData_GetGimmickWork(GameSystem_GetSaveData(temp->fsys));
	a_gmk_work = (ARCEUS_GMK_WORK*)GIMMICKWORK_Get(work, FLD_GIMMICK_ARCEUS_GMK);

	//　踏んでるモードかで分岐
	switch(a_gmk_work->LightPoint){
	case 0:			//すべて通常
		break;
	case 1:			//ディア踏んでる
		mode[0] = 1;
		break;
	case 2:			//パル踏んでる
		mode[1] = 1;
		break;
	case 3:			//ギラ踏んでる
		mode[2] = 1;
		break;
	default:
		GF_ASSERT(0);
		break;
	}

	//再生フレームを変える
	for (i=0;i<OBJ_NUM;i++){
		rc = AnimeLoop(&temp->AnmWork[i], ANM_NUM, mode[i]);
	}
	//3D描画
	for (i=0;i<OBJ_NUM;i++){
		FLD_3DObjDraw( &temp->AnmWork[i].Obj );
	}

	if (rc){
		//リクエストフラグをクリア
		temp->AnmChgReq = 0;
	}

	//デバッグ
	{
		int now_frame[OBJ_NUM];

		for (i=0;i<OBJ_NUM;i++){
			GMK_ANM_WORK *anm_work = &temp->AnmWork[i];
			now_frame[i] = FLD_3DObjAnmGet( &anm_work->Anm[0] )/FX32_ONE;
			now_frame[i] %= (NORMAL_ANM_FRAME_END/FX32_ONE);
		}
		GF_ASSERT_MSG(now_frame[0]==now_frame[1],"%d_%d\n",now_frame[0], now_frame[1]);
		GF_ASSERT_MSG(now_frame[1]==now_frame[2],"%d_%d\n",now_frame[1], now_frame[2]);
		GF_ASSERT_MSG(now_frame[2]==now_frame[0],"%d_%d\n",now_frame[2], now_frame[0]);
	}
}

//==============================================================================
/**
 * アニメフレーム更新関数
 *
 * @param	anm			アニメポインタ配列
 * @param	anm_num		アニメ数
 * @param	inType			0:通常アニメ　1:自機が踏んでるときアニメ
 *
 * @retval  none
 */
//==============================================================================
static BOOL AnimeLoop(GMK_ANM_WORK *anm_work, const u8 anm_num, const u8 inType)
{
	u8 i;
	fx32 now_frame, start_frame, end_frame;
	u8 end_num = 0;
	u8 flg = 0;

	if (anm_work->NowType/*inType*/){
		start_frame = NORMAL_ANM_FRAME_END;
		end_frame = LIGHT_ANM_FRAME_END;
	}else{
		start_frame = 0;
		end_frame = NORMAL_ANM_FRAME_END;
	}
	for (i=0;i<anm_num;i++){
		//現在フレームを取得
		now_frame = FLD_3DObjAnmGet( &anm_work->Anm[i] );
		//次の再生でアニメが最終フレームに到達するか？
		if ( now_frame + FX32_ONE >= end_frame ){
			if (i!=0){
				//既にフラグが立っているはず
				GF_ASSERT(flg);
			}
			//開始フレーム移動フラグを立てる
			flg = 1;
		}
		FLD_3DObjAnmLoop( &anm_work->Anm[i], FX32_ONE );
		if (flg){
			anm_work->NowType = inType;
			if (anm_work->NowType){
				start_frame = NORMAL_ANM_FRAME_END;
			}else{
				start_frame = 0;
			}
			//開始フレームへ
			FLD_3DObjAnmSet( &anm_work->Anm[i], start_frame );
		}
	}

	if (flg){
		return TRUE;
	}

	return FALSE;
}

//==============================================================================
/**
 * アニメフレームセット
 *
 * @param	anm			アニメポインタ配列
 * @param	anm_num		アニメ数
 * @param	inFrame		フレーム
 *
 * @retval  none
 */
//==============================================================================
static void SetAnmFrame(FLD_3DOBJ_ANM *anm, const u8 anm_num, const u32 inFrame)
{
	u8 i;
	for (i=0;i<anm_num;i++){
		FLD_3DObjAnmSet( &anm[i], inFrame  );
	}
}

//==============================================================================
/**
 * アニメ切り替わり待ちイベント
 *
 * @param	event	イベントポインタ
 *
 * @return	BOOL	TRUE:イベント終了　FALSE:イベント継続
 */
//==============================================================================
static BOOL AnmChgWaitEvent(GMEVENT_CONTROL * event)
{
	FIELDSYS_WORK * fsys = FieldEvent_GetFieldSysWork(event);
	GMK_TEMP_WORK * work = (GMK_TEMP_WORK *)FieldEvent_GetSpecialWork(event);

	if (work->AnmChgReq == 0){
		return TRUE;
	}
	return FALSE;
}

